package de.tu_darmstadt.informatik.tk.lernsystem.dao;

import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.openrdf.model.URI;
import org.openrdf.model.ValueFactory;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.TupleQuery;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.query.resultio.TupleQueryResultWriter;
import org.openrdf.query.resultio.sparqlxml.SPARQLResultsXMLWriterFactory;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.object.ObjectConnection;
import org.openrdf.repository.object.RDFObject;
import org.openrdf.repository.object.config.ObjectRepositoryFactory;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.result.Result;
import org.openrdf.sail.Sail;
import org.openrdf.sail.memory.MemoryStore;
import org.openrdf.sail.rdbms.mysql.MySqlStore;

import de.tu_darmstadt.informatik.tk.lernsystem.concepts.User;
import de.tu_darmstadt.informatik.tk.lernsystem.util.Config;
import de.tu_darmstadt.informatik.tk.lernsystem.util.Md5;

/**
 * Base class for all DAO Objects using RDF Backend.
 * See settings.properties file for configuration options.
 */
public abstract class RdfDao {
	private static Repository repository = null;
	private static ObjectConnection connection = null;
	private static Sail store = null;
	private static Boolean forceMemoryStore = false;
	
	/**
	 * Recycles the memory store.
	 */
	public static void resetMemoryStore() {
		tearDown();
		setUp();
	}
	
	public static void setForceMemoryStore() {
		forceMemoryStore = true;
	}
	
	/**
	 * Sets up the internal memory store/connects to MySQL database.
	 */
	public static void setUp() {
		try {
			if (!forceMemoryStore && Config.getInstance().getBoolean("store.useMySQL")) {
				MySqlStore dbStore = new MySqlStore(Config.getInstance().getString("store.database"));
				dbStore.setServerName(Config.getInstance().getString("store.hostname"));
				dbStore.setUser(Config.getInstance().getString("store.username"));
				dbStore.setPassword(Config.getInstance().getString("store.password"));
				store = dbStore;
			} else {
				store = new MemoryStore();
			}
			repository = new SailRepository(store);
			repository = new ObjectRepositoryFactory().createRepository(repository);
			repository.initialize();
			connection = (ObjectConnection)repository.getConnection();
			
//			setupTestData();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Closes the connection.
	 */
	public static void tearDown() {
		try {
			if (connection != null) {
				connection.close();
			}
			if (store != null) {
				store.shutDown();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	protected static <T> List<T> transformResultToList(Result<T> result) {
		List<T> list = new ArrayList<T>();
		try {
			while (result.hasNext()) {
				list.add(result.next());
			}
		} catch (QueryEvaluationException e) {
			e.printStackTrace();
		}
		return list;
	}
	
	protected static ObjectConnection getConnection() {
		return connection;
	}
	
	protected static void queryRepository(String queryString, OutputStream outputStream) {
		TupleQuery graphQuery = null;
		TupleQueryResult tupleQueryResult = null;
		TupleQueryResultWriter writer = null;
		 
		try {
			// Evaluate query
			graphQuery = connection.prepareTupleQuery(QueryLanguage.SPARQL, queryString);
			tupleQueryResult = graphQuery.evaluate();
	
			// Create writer
			writer = new SPARQLResultsXMLWriterFactory().getWriter(outputStream); 
			writer.startQueryResult(tupleQueryResult.getBindingNames());
			while(tupleQueryResult.hasNext()) {
				writer.handleSolution(tupleQueryResult.next());
			}
			writer.endQueryResult();
			
			// Close query result;
			tupleQueryResult.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Creates a new RDF object.
	 * @param <T>
	 * @param clazz Desired class
	 * @param id Desired ID
	 * @return Created object
	 */
	protected static <T> T createObject(Class<T> clazz, String id) {
		ValueFactory valueFactory = connection.getValueFactory();
		URI uri = valueFactory.createURI(id);
		RDFObject object = connection.getObjectFactory().createObject(uri);
		T result = null;
		try {
			result = (T)connection.addDesignation(object, clazz);
		} catch (RepositoryException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	/**
	 * Generates a random URN-ID.
	 * @return
	 */
	protected static String getRandomId() {
		return "urn:"+Md5.getHash(new Long(System.currentTimeMillis()).toString());
	}
	
	// TODO Remove later
	/*
	private static void setupTestData() {
		User u1 = UserDao.createUser("http://www.user1.com");
		u1.setUsername("user1");
		u1.setCredentialToken("user1");
		
		User u2 = UserDao.createUser("http://www.user2.com");
		u2.setUsername("user2");
		u2.setCredentialToken("user2");
	}
	*/
}
